package short_drama

import (
	"context"
	"database/sql"
	"encoding/json"
	"errors"
	"fmt"
	"strconv"
	"strings"
	"video-api/internal/consts"
	"video-api/internal/dao"
	"video-api/internal/middleware"
	"video-api/internal/model/entity"
	"video-api/internal/model/input"
	"video-api/internal/model/out"
	"video-api/internal/service"
	"video-api/utility/cdn_url"
	"video-api/utility/metrics"
	"video-api/utility/queue"
	"video-api/utility/queue/job_type"
	"video-api/utility/redis_client"

	"github.com/gogf/gf/v2/errors/gerror"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/os/gtime"
	"github.com/hibiken/asynq"
)

func init() {
	service.RegisterShortDramaService(&sShortDrama{
		redisCfg: &redis_client.RedisConfigDefault,
	})
}

type sShortDrama struct {
	redisCfg *redis_client.RedisConfig
}

func (s *sShortDrama) GetList(ctx context.Context, in *input.ShortDramaList) (shortDrama []*out.ShortDramaEntity, count int, err error) {
	if in.Page <= 0 {
		in.Page = 1
	}
	if in.PageSize <= 0 {
		in.PageSize = 10
	}
	//// 查询短剧配置
	//sdc := make([]entity.ShortDramaConfig, 0)
	//dao.ShortDramaConfig.Ctx(ctx).Where("product_id = ? AND language_id = ?", product.Id, in.Language).Scan(&sdc)
	sdModel := dao.ShortDrama.Ctx(ctx).Fields("short_drama.*")
	sdCountModel := dao.ShortDrama.Ctx(ctx)
	if in.ProductPkg != "" || in.Language != "" {
		sdModel = sdModel.LeftJoin("short_drama_config sdc", "sdc.short_drama_id=short_drama.id")
		sdCountModel = sdCountModel.LeftJoin("short_drama_config sdc", "sdc.short_drama_id=short_drama.id")
		//if in.ProductPkg != "" {
		//	sdModel = sdModel.Where("sdc.product_pkg_name=?", in.ProductPkg)
		//	sdCountModel = sdCountModel.Where("sdc.product_pkg_name=?", in.ProductPkg)
		//}
		if in.DeviceType != "" {
			product := new(entity.Product)
			err = dao.Product.Ctx(ctx).Where("product_device_type = ?", in.DeviceType).Scan(&product)
			if err != nil && errors.Is(err, sql.ErrNoRows) {
				return nil, 0, err
			}
			sdModel = sdModel.Where("sdc.product_id=?", product.Id)
			sdCountModel = sdCountModel.Where("sdc.product_id=?", product.Id)
		}
		if in.Language != "" {
			sdModel = sdModel.Where("sdc.language_id=?", in.Language)
			sdCountModel = sdCountModel.Where("sdc.language_id=?", in.Language)
		}
	}
	// 模糊搜索
	if len(in.SearchString) != 0 {
		sdModel = sdModel.WhereLike("short_name", "%"+in.SearchString+"%").WhereOrLike("description", "%"+in.SearchString+"%")
		sdCountModel = sdCountModel.WhereLike("short_name", "%"+in.SearchString+"%").WhereOrLike("description", "%"+in.SearchString+"%")
	}
	// 标签（近期热门，最新上映...）
	if in.Tag != 0 {
		tModel := dao.ShortDramaTags.Ctx(ctx)
		ids, err := tModel.Where("tag_id = ?", in.Tag).Fields("short_drama_id").Array()
		if err != nil {
			return nil, 0, err
		}
		sdModel = sdModel.WhereIn("id", ids)
		sdCountModel = sdCountModel.WhereIn("id", ids)
	}
	if in.CategoriesId != 0 {
		cModel := dao.ShortDramaCategories.Ctx(ctx)
		idsc, err := cModel.Where("categories_id = ?", in.CategoriesId).Fields("short_drama_id").Array()
		if err != nil {
			return nil, 0, err
		}
		sdModel = sdModel.WhereIn("id", idsc)
		sdCountModel = sdCountModel.WhereIn("id", idsc)
	}
	err = sdModel.Page(in.Page, in.PageSize).Scan(&shortDrama)
	if err != nil {
		g.Log().Error(ctx, "get short drama list error.", err)
		return nil, 0, err
	}
	count, err = sdCountModel.Count()
	if err != nil {
		g.Log().Error(ctx, "get short drama list count error.", err)
		return nil, 0, err
	}
	// 查询每个短剧的tags
	tModel := dao.ShortDramaTags.Ctx(ctx)
	for i, drama := range shortDrama {
		tags, err := tModel.
			LeftJoin("tags t", "t.id = short_drama_tags.tag_id").
			Where("short_drama_tags.short_drama_id = ?", drama.Id).
			Fields("t.title").
			Array()
		if err != nil {
			g.Log().Error(ctx, "get tags error for drama:", drama.Id, err)
			continue
		}
		// 将tag titles转换为字符串数组
		tagTitles := make([]string, len(tags))
		for j, tag := range tags {
			tagTitles[j] = tag.String()
		}
		shortDrama[i].Tags = tagTitles
	}
	s.FormatShortDrama(ctx, shortDrama)
	return shortDrama, count, nil
}

func (s *sShortDrama) EpisodesPlayEvent(ctx context.Context, in *input.ShortDramaEpisodesPlayEvent) (err error) {
	userInfo := middleware.GetUserInfo(ctx)
	if userInfo != nil && userInfo.UserId != 0 {
		// 登录才记录播放历史
		playTime := gtime.Now()
		data := queue.AddShortDramaHistory{
			UserId:       userInfo.UserId,
			ShortDramaId: in.ShortDramaId,
			EpisodesId:   in.EpisodesId,
			PlayTime:     playTime,
		}
		// err := queue.PushTask(ctx, "add_history_record", data)
		// if err != nil {
		// 	g.Log().Error(ctx, "push task err:", err)
		// }

		//asynq投递
		// redisCfg := redis_client.RedisConfigDefault
		asynqClient := asynq.NewClient(asynq.RedisClientOpt{Addr: s.redisCfg.Addr, Password: s.redisCfg.Password, DB: 1})
		jsonData, err := json.Marshal(data)
		if err != nil {
			g.Log().Error(ctx, "push task json marshal err:", err)
		}
		_, err = asynqClient.Enqueue(asynq.NewTask(job_type.ScheduleAddDramaHistory, jsonData), asynq.MaxRetry(3))
		if err != nil {
			g.Log().Error(ctx, "push task err:", err)
		}
	}
	return nil

}

func (s *sShortDrama) RmHistoryRecord(ctx context.Context, id int) (bool, error) {
	userInfo := middleware.GetUserInfo(ctx)
	if userInfo != nil && userInfo.UserId != 0 {
		// 登录才能删除播放历史
		data := queue.DelShortDramaHistory{
			UserId:   userInfo.UserId,
			RecordId: int64(id),
		}
		// err := queue.PushTask(ctx, "del_history_record", data)
		// if err != nil {
		// 	g.Log().Error(ctx, "push task err:", err)
		// }

		//asynq投递
		asynqClient := asynq.NewClient(asynq.RedisClientOpt{Addr: s.redisCfg.Addr, Password: s.redisCfg.Password, DB: 1})
		jsonData, err := json.Marshal(data)
		if err != nil {
			g.Log().Error(ctx, "push task json marshal err:", err)
			return false, err
		}
		_, err = asynqClient.Enqueue(asynq.NewTask(job_type.ScheduleDelDramaHistory, jsonData), asynq.MaxRetry(3))
		if err != nil {
			g.Log().Error(ctx, "push task err:", err)
			return false, err
		}
	}
	return true, nil
}

func (s *sShortDrama) GetDetail(ctx context.Context, in *input.ShortDramaDetail) (shortDrama *out.ShortDramaEntity, err error) {
	if in.ShortDramaId == 0 {
		return nil, gerror.New("short drama err:short drama id is 0.")
	}
	//TODO userid登录获取
	userInfo := middleware.GetUserInfo(ctx)
	if userInfo == nil || userInfo.UserId == 0 {
		return nil, gerror.New("user not exist")
	}
	metrics.HttpShortDramaCounts.WithLabelValues(strconv.FormatInt(userInfo.UserId, 10), strconv.FormatInt(int64(in.ShortDramaId), 10)).Inc() //统计请求短剧次数
	sdModel := dao.ShortDrama.Ctx(ctx)
	result, err := sdModel.
		LeftJoin(dao.ShortDramaEpisodes.Table(),
			"short_drama_episodes.short_drama_id = short_drama.id").
		LeftJoin(dao.ShortDramaUnlocks.Table(), fmt.Sprintf("(short_drama_unlocks.short_drama_episode_id = short_drama_episodes.id AND short_drama_unlocks.user_id = %d)", userInfo.UserId)).
		Where("short_drama.id = ?", in.ShortDramaId).
		Order("short_drama_episodes.order ASC").
		Fields(
			"short_drama.*",
			"short_drama_episodes.id AS episode_id",
			"short_drama_episodes.short_drama_id AS short_drama_id",
			"short_drama_episodes.title AS episode_title",
			"short_drama_episodes.description AS episode_description",
			"short_drama_episodes.video_url AS episode_video_url",
			"short_drama_episodes.order AS episode_order",
			"short_drama_episodes.duration AS episode_duration",
			"short_drama_unlocks.id AS short_drama_episodes_unlock_id",
		).
		All()
	if err != nil {
		g.Log().Error(ctx, "get short detail error.", err)
		return nil, err
	}
	if result.Len() == 0 {
		return nil, gerror.New("result is null")
	}
	// 获取短剧配置
	shortDramaConfig := new(entity.ShortDramaConfig)
	err = dao.ShortDramaConfig.Ctx(ctx).Where("short_drama_id = ?", in.ShortDramaId).Scan(&shortDramaConfig)
	if err != nil {
		return nil, err
	}
	var episodes []out.ShortDramaEpisodesEntity
	for _, row := range result {
		if row["episode_id"].Int64() != 0 {
			shortDramaEpisodesEntity := out.ShortDramaEpisodesEntity{
				Id:           row["episode_id"].Int64(),
				ShortDramaId: row["short_drama_id"].Int64(),
				Title:        row["short_name"].String(), // todo 暂时用短剧名
				Description:  row["episode_description"].String(),
				Order:        row["episode_order"].Int(),
				Duration:     row["episode_duration"].Int(),
				UnlockId:     row["short_drama_episodes_unlock_id"].Int(),
				CostCoin:     shortDramaConfig.CostCoin,
				EpisodeImg:   row["cover_img"].String(),
			}

			// 判断是否是可以观看集数
			if shortDramaEpisodesEntity.Order <= shortDramaConfig.FreeNum || shortDramaEpisodesEntity.UnlockId > 0 {
				shortDramaEpisodesEntity.VideoUrl = row["episode_video_url"].String()
				shortDramaEpisodesEntity.IsUnLock = true
			}
			episodes = append(episodes, shortDramaEpisodesEntity)
		}
	}
	shortDrama = new(out.ShortDramaEntity)
	if result.Len() > 0 {
		firstRow := result[0]
		*shortDrama = out.ShortDramaEntity{
			Id:                firstRow["id"].Int64(),
			ShortName:         firstRow["short_name"].String(),
			Description:       firstRow["description"].String(),
			CoverImg:          firstRow["cover_img"].String(),
			CategoryId:        firstRow["category_id"].Int(),
			Publisher:         firstRow["publisher"].String(),
			PublishTime:       firstRow["publish_time"].GTime(),
			Sex:               firstRow["sex"].String(),
			ChapterNum:        firstRow["chapter_num"].Int(),
			AgeLevel:          firstRow["age_level"].String(),
			Completed:         firstRow["completed"].Int(),
			Status:            firstRow["status"].String(),
			LastestUpdateTime: firstRow["lastest_update_time"].GTime(),
			Price:             firstRow["price"].Float64(),
			OriginalPrice:     firstRow["original_price"].Float64(),
			Language:          firstRow["language"].String(),
			Episodes:          episodes,
		}
	}
	s.FormatShortDrama(ctx, []*out.ShortDramaEntity{shortDrama})
	return shortDrama, nil
}

func (s *sShortDrama) Collect(ctx context.Context, in *input.ShortDramaCollect) (isMember bool, err error) {
	// TODO事务处理
	if in.ShortDramaId == 0 {
		return false, gerror.New("short drama err:short drama id is 0.")
	}
	userInfo := middleware.GetUserInfo(ctx)
	if userInfo == nil || userInfo.UserId == 0 {
		return false, gerror.New("user not exist")
	}
	//该用户是否已收藏该短剧
	key := consts.SHORT_DRAMA_COLLECT + ":" + strconv.Itoa(int(userInfo.UserId))
	member := redis_client.RedisClient.SIsMember(ctx, key, in.ShortDramaId)
	if member.Err() != nil {
		g.Log().Error(ctx, "err.", member.Err())
		return false, member.Err()
	}
	// 判断数据库是否已经存在该收藏
	shortDramaRecord := new(entity.ShortDramaRecord)
	recordModel := dao.ShortDramaRecord.Ctx(ctx)
	err = recordModel.Where("short_drama_id = ? AND user_id = ? AND category = ?", in.ShortDramaId, userInfo.UserId, consts.COLLECT).
		Scan(shortDramaRecord)
	if err != nil && !errors.Is(err, sql.ErrNoRows) {
		g.Log().Error(ctx, "sShortDrama Collect getRecord err ", err)
		return false, err
	}
	if shortDramaRecord.Id == 0 {
		_, err = recordModel.Save(&entity.ShortDramaRecord{
			ShortDramaId: int64(in.ShortDramaId),
			UserId:       userInfo.UserId,
			Category:     consts.COLLECT,
		})
		if err != nil {
			g.Log().Error(ctx, "sShortDrama Collect saveRecord err ", err)
			return false, err
		}
	}
	if member.Val() == true {
		//已收藏,则取消收藏
		_, err = recordModel.Where("id = ?", shortDramaRecord.Id).Data(g.Map{
			"deleted_at": gtime.Now(),
		}).Update()
		if err != nil {
			g.Log().Error(ctx, "collect short delete err.", err)
			return false, err
		}
		rem := redis_client.RedisClient.SRem(ctx, key, in.ShortDramaId)
		if rem.Err() != nil {
			g.Log().Error(ctx, "cancel collect short drama err.", member.Err())
			return true, rem.Err()
		}
		if rem.Val() == 1 {
			return false, nil
		} else {
			return true, nil
		}

	} else {
		//未收藏则收藏
		add := redis_client.RedisClient.SAdd(ctx, key, in.ShortDramaId)
		if add.Err() != nil {
			g.Log().Error(ctx, "collect short drama err.", add.Err())
			return false, add.Err()
		}
		if add.Val() == 1 {
			return true, nil
		} else {
			return false, nil
		}
	}
}

func (s *sShortDrama) GetCollectList(ctx context.Context, in *input.ShortDramaCollectList) (shortDramas []*out.ShortDramaEntity, err error) {
	userInfo := middleware.GetUserInfo(ctx)
	in.UserId = int(userInfo.UserId)
	key := consts.SHORT_DRAMA_COLLECT + ":" + strconv.Itoa(in.UserId)
	members := redis_client.RedisClient.SMembers(ctx, key)
	if members.Err() != nil {
		g.Log().Error(ctx, "get collect short drama list error.", members.Err())
		return nil, members.Err()
	}
	sdModel := dao.ShortDrama.Ctx(ctx)
	err = sdModel.WhereIn("id", members.Val()).Scan(&shortDramas)
	if err != nil {
		g.Log().Error(ctx, "get collect short drama list error.", err)
		return nil, err
	}
	s.FormatShortDrama(ctx, shortDramas)
	return shortDramas, nil
}

func (s *sShortDrama) GetShortDramaEpisodes(ctx context.Context, param *input.ShortDramaEpisodes) (res []*out.ShortDramaEpisodesDetail, err error) {
	sdModel := dao.ShortDramaEpisodes.Ctx(ctx)
	episodes := make([]*out.ShortDramaEpisodesDetail, 0)
	if len(param.Ids) > 0 {
		sdModel = sdModel.With(entity.ShortDrama{}).WhereIn("short_drama_id", param.Ids)
	}
	err = sdModel.Scan(&episodes)
	if err != nil {
		return nil, err
	}
	return episodes, nil
}

func (s *sShortDrama) GetHistoryList(ctx context.Context, in *input.ShortDramaHistoryList) (shortDramas []*out.ShortDramaHistoryEntity, count int, err error) {
	userInfo := middleware.GetUserInfo(ctx)
	if userInfo == nil || userInfo.UserId == 0 {
		return nil, 0, gerror.New("user not exist")
	}
	in.UserId = int(userInfo.UserId)
	key := consts.SHORT_DRAMA_HISTORY + ":" + strconv.Itoa(in.UserId)
	zRange := redis_client.RedisClient.ZRevRange(ctx, key, int64((in.Page-1)*in.PageSize), int64(in.Page*in.PageSize-1)) //返回数组切片
	// fmt.Println("zRange:", zRange.Val())
	if zRange.Err() != nil {
		return nil, 0, zRange.Err()
	}
	countRes, err := redis_client.RedisClient.ZCard(ctx, key).Result()
	if err != nil {
		return nil, 0, err
	}
	count = int(countRes)
	prefix := g.Cfg().MustGet(ctx, "cdnurl.url").String()

	type ShortDramaHistory struct {
		Id           int64       `json:"id"                description:"记录ID"`
		ShortDramaId int64       `json:"shortDramaId"      description:"短剧ID"` // 短剧ID
		ShortName    string      `json:"shortName"         description:"短剧标题"`
		Order        int         `json:"order"             description:""`
		Description  string      `json:"description"       description:""`
		CoverImg     string      `json:"coverImg"          description:"封面图片"`
		Tags         string      `json:"tags"              description:"标签"`
		PlayAt       *gtime.Time `json:"playAt"            description:"播放时间"`
		EpisodesId   int         `json:"episodesId"          description:"集数id"`
	}

	for _, x := range zRange.Val() {
		//x扫进ShortDramaHistoryEntity
		//var drama out.ShortDramaHistoryEntity
		var drama ShortDramaHistory
		// 获取zset score
		score, err := redis_client.RedisClient.ZScore(ctx, key, x).Result()
		if err != nil {
			g.Log().Error(ctx, "get collect short drama history list error.", err)
			continue
		}
		// 时间戳转时间
		drama.PlayAt = gtime.NewFromTimeStamp(int64(score))
		// 将 JSON 字符串解析成结构体
		err = json.Unmarshal([]byte(x), &drama)
		if err != nil {
			g.Log().Error(ctx, "get collect short drama history list error.", err)
			//continue
		}
		urlInfo, err := cdn_url.CdnUrl(prefix, drama.CoverImg)
		if err != nil {
			g.Log().Error(ctx, "get collect short drama history list error.", err)
		}
		drama.CoverImg = urlInfo.Url
		dramaRes := out.ShortDramaHistoryEntity{
			Id:           drama.Id,
			ShortDramaId: drama.ShortDramaId,
			ShortName:    drama.ShortName,
			Order:        drama.Order,
			Description:  drama.Description,
			CoverImg:     drama.CoverImg,
			PlayAt:       drama.PlayAt,
			EpisodesId:   drama.EpisodesId,
		}
		if drama.Tags != "" {
			tagList := strings.Split(drama.Tags, ",")
			dramaRes.Tags = tagList
		}
		shortDramas = append(shortDramas, &dramaRes)
	}
	return shortDramas, count, nil
}

func (s *sShortDrama) FormatShortDrama(ctx context.Context, list []*out.ShortDramaEntity) []*out.ShortDramaEntity {
	processUrl := func(url string) (string, int64, error) {
		if len(url) == 0 {
			return "", 0, nil
		}
		prefix := g.Cfg().MustGet(ctx, "cdnurl.url").String()
		cdnUrl, err := cdn_url.CdnUrl(prefix, url)
		if err != nil {
			return "", 0, err
		}
		return cdnUrl.Url, cdnUrl.Timestamp, nil
	}
	for i, v := range list {
		if len(v.CoverImg) != 0 {
			cdnUrl, _, err := processUrl(v.CoverImg)
			if err != nil {
				g.Log().Error(ctx, "Error fetching CDN URL for cover image: %v, err: %v", v.CoverImg, err)
				continue
			}
			list[i].CoverImg = cdnUrl
		}
		for i2, v2 := range v.Episodes {
			if len(v2.VideoUrl) != 0 {
				cdnUrl2, expired, err := processUrl(v2.VideoUrl)
				if err != nil {
					g.Log().Error(ctx, "Error fetching CDN URL for episode video URL: %v, err: %v", v2.VideoUrl, err)
					continue
				}
				list[i].Episodes[i2].VideoUrl = cdnUrl2
				list[i].Episodes[i2].VideoUrlTime = expired
			}
			if len(v2.EpisodeImg) != 0 {
				cdnUrl3, _, err := processUrl(v2.EpisodeImg)
				if err != nil {
					g.Log().Error(ctx, "Error fetching CDN URL for episode img URL: %v, err: %v", v2.EpisodeImg, err)
					continue
				}
				list[i].Episodes[i2].EpisodeImg = cdnUrl3
			}
		}
	}
	return list
}

// GetShortDramaEpisodeById 获取单集剧集信息
func (s *sShortDrama) GetShortDramaEpisodeById(ctx context.Context, param *input.ShortDramaEpisodes) (*out.ShortDramaEpisodesDetail, error) {
	episode := new(out.ShortDramaEpisodesDetail)
	if param.EpisodesId == 0 {
		return episode, nil
	}
	// .With(entity.ShortDrama{})
	sdModel := dao.ShortDramaEpisodes.Ctx(ctx).Where("id = ?", param.EpisodesId)
	err := sdModel.Scan(&episode)
	if err != nil && !errors.Is(err, sql.ErrNoRows) {
		return nil, err
	}
	prefix := g.Cfg().MustGet(ctx, "cdnurl.url").String()
	urlInfo, _ := cdn_url.CdnUrl(prefix, episode.VideoUrl)
	episode.VideoUrl = urlInfo.Url
	episode.VideoUrlTime = urlInfo.Timestamp
	return episode, nil
}
